import axios, { AxiosRequestHeaders } from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import router from '@/router'
import qs from 'qs'

const request = axios.create({
  // 配置选项
  // baseURL
  // timeout
})

function redirectLogin () {
  router.push({
    name: 'login',
    query: { redirect: router.currentRoute.fullPath }
  })
}

function refreshToken () {
  return axios.create()({
    method: 'POST',
    url: '/front/user/refresh_token',
    data: qs.stringify({
      refreshtoken: store.state.user.refresh_token
    })
  })
}

// 请求拦截器
request.interceptors.request.use(config => {
  const { user } = store.state
  if (user && user.access_token) {
    (config.headers as AxiosRequestHeaders).Authorization = user.access_token
  }
  return config
}, err => {
  return Promise.reject(err)
})

// 控制刷新 token 的状态
// 防止多条请求并发时，多次刷新token
let isRfreshing = false
// 存储刷新 token 期间过来的 401请求
let requests:any[] = []
// 响应拦截器
request.interceptors.response.use(config => {
  // 所有 2xx 的状态码进入（请求响应成功）
  // 服务端如果是自定义状态码，错误也返回成功，那么错误处理就写在这里
  return config
}, async err => {
  // 超出 2xx 的状态码进入（请求响应失败）
  console.dir(err) // 完整错误信息
  // 服务端如果是使用http状态码，那么错误处理就写在这里

  if (err.response) {
    // 请求发出去收到响应了，状态码超出 2xx 范围
    // 401 登陆验证失败 403 没权限 404 请求得资源不存在，找不到
    const { status } = err.response
    if (status === 400) {
      Message.error('请求参数错误')
    } else if (status === 401) {
      // 没 token ，token无效，token过期
      // 如果有refresh_ token 则尝试使用refresh_ token获取新的 access_ token
      // - 成功了 → 把本次失败的请求重新发出去
      // - 失败了 → 跳转登录页重新登录获取新的 token
      // 如果没有，则直接跳转登录页

      // 没token
      if (!store.state.user) {
        redirectLogin()
        return Promise.reject(err)
      }

      // 刷新 token
      if (!isRfreshing) {
        isRfreshing = true

        return refreshToken().then(res => {
          // 刷新成功，实质没成功，要用 success字段判断
          if (!res.data.success) {
            throw new Error('刷新token 失败')
          }
          // 刷新成功去改 token，并再次发要发的请求
          store.commit('setUser', res.data.content)
          // 刷新成功 调用挂起的请求
          requests.forEach(cb => cb())
          requests = []
          return request(err.config)
        }).catch(_error => {
          // 刷新失败，返回登录页
          console.log(_error)
          store.commit('setUser', null)
          redirectLogin()
          return Promise.reject(err)
        }).finally(() => { // 无论刷新成功失败,都重置刷新状态
          isRfreshing = false
        })
      }
      // 在刷新状态下把请求挂起，放到 requests 数组中
      // promise可以控制完成状态，只有调用 requests 时才完成
      return new Promise(resolve => {
        requests.push(() => {
          resolve(request(err.config))
        })
      })

      // try {
      // 如果使用 request请求，当前这个请求也可能失败，导致重复刷新,应该使用 axios.create()
      // const { data } = await axios.create()({
      //   method: 'POST',
      //   url: '/front/user/refresh_token',
      //   data: qs.stringify({
      //     refreshtoken: store.state.user.refresh_token
      //   })
      // })
      // 成功修改token，并再发一次请求
      // store.commit('setUser', data.content)
      // return request(err.config) // err.config存请求信息
      // } catch (err) {
      // 失败直接跳转
      // store.commit('setUser', null)
      // redirectLogin()
      // return Promise.reject(err)
      // }
    } else if (status === 403) {
      Message.error('没有权限，请联系管理员')
    } else if (status === 404) {
      Message.error('请求资源不存在')
    } else if (status >= 500) {
      Message.error('服务端错误，请联系管理员')
    }
  } else if (err.request) {
    // 请求发出去没有收到响应，（请求超时，或网络断开）
    Message.error('请求超时，请刷新重试')
  } else {
    // 在设置请求时发生了一些事情，触发了一个错误
    Message.error(`请求失败: ${err.message}`)
  }

  // 把请求失败的错误对象继续抛出，扔给下一个上一个调用者
  return Promise.reject(err)
})

export default request
